﻿#region Copyright (c) 2013-04, Olaf Kober <amarok.projects@gmail.com>
//================================================================================
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//================================================================================
#endregion

#if TESTS

using System;
using System.Threading;
using NUnit.Framework;


namespace Amarok.Agents
{
	public partial class Test_Agent
	{
		#region Publish(Message)

		[Test]
		public void Test_Publish()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			agent.TestPublish(message);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.Null);
		}

		[Test]
		public void Test_Publish_Message()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);

			Assert.Throws<ArgumentNullException>(() =>
				{
					agent.TestPublish(null);
				});
		}

		#endregion

		#region Publish(Object, Message)

		[Test]
		public void Test_Publish_Label()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			message.Label = "BLAH";
			agent.TestPublish("COM1", message);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_Publish_Label_NullLabel()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);

			Assert.Throws<ArgumentNullException>(() =>
				{
					agent.TestPublish(null, new TestMessage());
				});
		}

		[Test]
		public void Test_Publish_Label_NullMessage()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);

			Assert.Throws<ArgumentNullException>(() =>
				{
					agent.TestPublish("BLAH", null);
				});
		}

		[Test]
		public void Test_Publish_With_Label_Specified()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			agent.TestPublish("COM2", message);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.EqualTo("COM2"));
		}

		[Test]
		public void Test_Publish_Label_Specified_And_Message_With_Label()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			message.Label = "COM99";
			agent.TestPublish("COM1", message);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		#endregion


		#region PublishAfter(TimeSpan, Message)

		[Test]
		public void Test_PublishAfter_TimeSpan()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			agent.TestPublishAfter(TimeSpan.FromMilliseconds(10), message);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.Null);
		}

		#endregion

		#region PublishAfter(Int32, Message)

		[Test]
		public void Test_PublishAfter_Int32()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			agent.TestPublishAfter(10, message);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.Null);
		}

		#endregion

		#region PublishAfter(TimeSpan, Message, CancellationToken)

		[Test]
		public void Test_PublishAfter_TimeSpan_CancellationToken()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);
			var source = new CancellationTokenSource();

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			agent.TestPublishAfter(TimeSpan.FromMilliseconds(10), message, source.Token);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.Null);
		}

		#endregion

		#region PublishAfter(Int32, Message, CancellationToken)

		[Test]
		public void Test_PublishAfter_Int32_CancellationToken()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);
			var source = new CancellationTokenSource();

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			agent.TestPublishAfter(10, message, source.Token);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.Null);
		}

		#endregion


		#region PublishAfter(TimeSpan, Object, Message)

		[Test]
		public void Test_PublishAfter_Label_TimeSpan()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			message.Label = "BLAH";
			agent.TestPublishAfter(TimeSpan.FromMilliseconds(10), "COM1", message);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		#endregion

		#region PublishAfter(Int32, Object, Message)

		[Test]
		public void Test_PublishAfter_Label_Int32()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			message.Label = "BLAH";
			agent.TestPublishAfter(10, "COM1", message);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		#endregion

		#region PublishAfter(TimeSpan, Object, Message, CancellationToken)

		[Test]
		public void Test_PublishAfter_Label_TimeSpan_CancellationToken()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);
			var source = new CancellationTokenSource();

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			message.Label = "BLAH";
			agent.TestPublishAfter(TimeSpan.FromMilliseconds(10), "COM1", message, source.Token);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		#endregion

		#region PublishAfter(Int32, Object, Message, CancellationToken)

		[Test]
		public void Test_PublishAfter_Label_Int32_CancellationToken()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);
			var source = new CancellationTokenSource();

			agentInterface.Subscribe(
				typeof(TestMessage),
				subscriber.ToAgentReference());

			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(0));

			var message = new TestMessage();
			message.Label = "BLAH";
			agent.TestPublishAfter(10, "COM1", message, source.Token);

			Helper.WaitForCondition(5000, 10, () => subscriber.MessageHandlerCalled == 1);
			Assert.That(subscriber.MessageHandlerCalled, Is.EqualTo(1));
			Assert.That(message.Label, Is.EqualTo("COM1"));
		}

		#endregion


		#region PublishOperation(Operation)

		[Test]
		public void Test_PublishOperation()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestOperation),
				subscriber.ToAgentReference());

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(0));

			var operation = new TestOperation();
			agent.TestPublishOperation(operation, null).Wait(5000);

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(1));
			Assert.That(operation.Completion.Result, Is.EqualTo("done"));
			Assert.That(operation.Label, Is.Null);
		}

		[Test]
		public void Test_PublishOperation_Progress()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestOperation),
				subscriber.ToAgentReference());

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(0));

			var progressValue = 0;
			var progressSignal = new ManualResetEvent(false);
			var operation = new TestOperation();

			agent.TestPublishOperation(
				operation,
				p =>
				{
					progressValue = p;
					progressSignal.Set();
				})
				.Wait(5000);

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(1));
			Assert.That(operation.Completion.Result, Is.EqualTo("done"));
			Assert.That(operation.Label, Is.Null);
			Assert.That(progressSignal.WaitOne(5000), Is.True);
			Assert.That(progressValue, Is.EqualTo(75));
		}

		[Test]
		public void Test_PublishOperation_NullMessage()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);

			Assert.Throws<ArgumentNullException>(() =>
			{
				agent.TestPublishOperation<String, Int32>(null, null);
			});
		}

		#endregion

		#region PublishOperation(Operation, CancellationToken)

		[Test]
		public void Test_PublishOperation_CancellationToken()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestOperation),
				subscriber.ToAgentReference());

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(0));

			var operation = new TestOperation();
			var cancelSource = new CancellationTokenSource();
			agent.TestPublishOperation(operation, cancelSource.Token, null).Wait(5000);

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(1));
			Assert.That(operation.Completion.Result, Is.EqualTo("done"));
			Assert.That(operation.Label, Is.Null);

			Assert.That(operation.CancellationToken.IsCancellationRequested, Is.False);
			cancelSource.Cancel();
			Assert.That(operation.CancellationToken.IsCancellationRequested, Is.True);
		}

		#endregion

		#region PublishOperation(Object, Operation)

		[Test]
		public void Test_PublishOperation_Label()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestOperation),
				subscriber.ToAgentReference());

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(0));

			var operation = new TestOperation();
			operation.Label = "BLAH";
			agent.TestPublishOperation("COM1", operation, null).Wait(5000);

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(1));
			Assert.That(operation.Completion.Result, Is.EqualTo("done"));
			Assert.That(operation.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishOperation_Label_Progress()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestOperation),
				subscriber.ToAgentReference());

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(0));

			var progressValue = 0;
			var progressSignal = new ManualResetEvent(false);
			var operation = new TestOperation();
			operation.Label = "BLAH";
			agent.TestPublishOperation(
				"COM1",
				operation,
				p =>
				{
					progressValue = p;
					progressSignal.Set();
				})
				.Wait(5000);

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(1));
			Assert.That(operation.Completion.Result, Is.EqualTo("done"));
			Assert.That(operation.Label, Is.EqualTo("COM1"));
			Assert.That(progressSignal.WaitOne(5000), Is.True);
			Assert.That(progressValue, Is.EqualTo(75));
		}

		[Test]
		public void Test_PublishOperation_With_Label_Specified()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestOperation),
				subscriber.ToAgentReference());

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(0));

			var operation = new TestOperation();
			agent.TestPublishOperation("COM1", operation, null).Wait(5000);

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(1));
			Assert.That(operation.Completion.Result, Is.EqualTo("done"));
			Assert.That(operation.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishOperation_Label_Specified_And_Message_With_Label()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestOperation),
				subscriber.ToAgentReference());

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(0));

			var operation = new TestOperation();
			operation.Label = "COM99";
			agent.TestPublishOperation("COM1", operation, null).Wait(5000);

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(1));
			Assert.That(operation.Completion.Result, Is.EqualTo("done"));
			Assert.That(operation.Label, Is.EqualTo("COM1"));
		}

		[Test]
		public void Test_PublishOperation_Label_NullLabel()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var operation = new TestOperation();

			Assert.Throws<ArgumentNullException>(() =>
				{
					agent.TestPublishOperation(null, operation, null);
				});
		}

		#endregion

		#region PublishOperation(Object, Operation, CancellationToken)

		[Test]
		public void Test_PublishOperation_Label_Progress_CancellationToken()
		{
			var environment = new MyAgentEnvironment();
			var options = new MyAgentOptions();
			var agent = new MyAgent(environment, options);
			var agentInterface = (IAgent)agent;
			var subscriber = new MySubscriberAgent(environment, options);

			agentInterface.Subscribe(
				typeof(TestOperation),
				subscriber.ToAgentReference());

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(0));

			var progressValue = 0;
			var progressSignal = new ManualResetEvent(false);
			var operation = new TestOperation();
			var cancelSource = new CancellationTokenSource();

			operation.Label = "BLAH";
			agent.TestPublishOperation(
				"COM1",
				operation,
				cancelSource.Token,
				p =>
				{
					progressValue = p;
					progressSignal.Set();
				})
				.Wait(5000);

			Assert.That(subscriber.OperationHandlerCalled, Is.EqualTo(1));
			Assert.That(operation.Completion.Result, Is.EqualTo("done"));
			Assert.That(operation.Label, Is.EqualTo("COM1"));
			Assert.That(progressSignal.WaitOne(5000), Is.True);
			Assert.That(progressValue, Is.EqualTo(75));

			Assert.That(operation.CancellationToken.IsCancellationRequested, Is.False);
			cancelSource.Cancel();
			Assert.That(operation.CancellationToken.IsCancellationRequested, Is.True);
		}

		#endregion

	}
}

#endif
